package uk.org.fuzelogik.eldaw.auth.openid;

import java.util.List;

import org.apache.wicket.Application;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.MessageException;

import uk.org.fuzelogik.eldaw.auth.DelegatedAuthenticationException;
import uk.org.fuzelogik.eldaw.auth.DelegatedAuthenticationStrategy;
import uk.org.fuzelogik.eldaw.wicket.component.IncompatibleApplicationException;


/**
 * Uses the OpenID protocol to perform delegated authentication.
 * 
 * @author cgdavies
 */
public class OpenIDAuthenticationStrategy extends
	DelegatedAuthenticationStrategy
{
	private DiscoveryInformation m_discoveryInformation;

	/**
	 * Checks that the <code>Application</code> instance implements the
	 * {@link IOpenIDAwareApplication} interface.
	 */
	private void verifyApplicationCompatibility()
	{
		if( !( Application.get() instanceof IOpenIDAwareApplication ) )
			throw new IncompatibleApplicationException(
				IOpenIDAwareApplication.class );
	}

	public String beginAuthentication( String userId )
		throws DelegatedAuthenticationException
	{
		verifyApplicationCompatibility();

		IOpenIDAwareApplication app = (IOpenIDAwareApplication)Application
			.get();
		ConsumerManager consumerManager = app.getConsumerManager();

		try
		{
			List discoveries = consumerManager.discover( userId );
			DiscoveryInformation discInfo = consumerManager
				.associate( discoveries );

			if( discInfo == null )
				throw new DelegatedAuthenticationException(
					"Failed to associate with any discoveries." );

			// Save the info in the session; we need to access this later.
			m_discoveryInformation = discInfo;

			AuthRequest authReq = consumerManager.authenticate( discInfo, app
				.getOpenIdReturnUrl() );
			
			return authReq.getDestinationUrl( true );
		}
		catch( DiscoveryException ex )
		{
			throw new DelegatedAuthenticationException( ex );
		}
		catch( ConsumerException ex )
		{
			throw new DelegatedAuthenticationException( ex );
		}
		catch( MessageException ex )
		{
			throw new DelegatedAuthenticationException( ex );
		}
	}

	public boolean isAuthenticationInProcess()
	{
		return ( m_discoveryInformation != null );
	}

	protected void endAuthentication()
	{
		// We don't need to hold onto this any more.
		m_discoveryInformation = null;
	}

	public DiscoveryInformation getDiscoveryInformation()
	{
		return m_discoveryInformation;
	}
}
